#include<iostream>
#include<string>
#include<cassert>
#include<unistd.h>
#include<sys/socket.h> 
#include<arpa/inet.h>        //字节序转换接口
#include<netinet/in.h>       //地质结构类型与协议类型宏头文件

class UdpSocket{
private:
  int _sockfd;
public:
  UdpSocket():_sockfd(-1) {}
  ~UdpSocket() {Close();}

  bool Socket()
  {
    //创建套接字
    _sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
    if(_sockfd<0){
      perror("socket error!\n");
      return false;
    }
    return true;
  }

  bool Bind(const std::string& ip,uint16_t port){
    //绑定地址信息
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;      //ipv4 地址类型
    addr.sin_port=htons(port);   //主机端口转换为网络端口
    addr.sin_addr.s_addr=inet_addr(ip.c_str());  //点分十进制IP转换为整型ip
    socklen_t len=sizeof(struct sockaddr_in);

    int ret=bind(_sockfd,(struct sockaddr*)&addr,len);
    if(ret<0){
       perror("bind error!\n");
       return false;
    }
    return true;
  }

  //接收数据
  bool Recv(std::string* body,std::string* peer_ip=NULL,uint16_t *peer_port=NULL){
    struct sockaddr_in peer;    //用来保存发送方地址信息
    socklen_t len=sizeof(struct sockaddr_in);
    char tmp[4096]={0};
    ssize_t ret=recvfrom(_sockfd,tmp,4096,0,(struct sockaddr*)&peer,&len);
    if(ret<0){
      perror("recvfrom error!\n");
      return false;
    }
    if(peer_ip!=NULL) *peer_ip=inet_ntoa(peer.sin_addr);
    if(peer_port!=NULL) *peer_port=ntohs(peer.sin_port);
    body->assign(tmp,ret);       //从 tmp 中截取 ret 长度数据放入 body
    return true;
  }

  //发送数据
  bool Send(const std::string &body,const std::string& peer_ip,uint16_t peer_port){
    struct sockaddr_in addr;
    addr.sin_family=AF_INET;
    addr.sin_port=htons(peer_port);
    addr.sin_addr.s_addr=inet_addr(peer_ip.c_str());
    socklen_t len=sizeof(struct sockaddr_in);

    ssize_t ret = sendto(_sockfd,body.c_str(),body.size(),0,(struct sockaddr*)&addr,len);
    if(ret<0){
      perror("endto error!\n");
      return false;
    }
    return true;
  }
 
  //关闭套接字
  bool Close()
  {
     if(_sockfd!=-1){
       close(_sockfd); _sockfd=-1;
     }
     return true;
  }

};


